package com.sh.data.engine.domain.integration.api.webservice.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.sh.data.engine.common.enumDefinition.ApiTaskStatus;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.JsonPathUtil;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.base.model.PageResult;
import com.sh.data.engine.domain.base.model.UserContext;
import com.sh.data.engine.domain.base.model.datax.ApiWsJobContentReaderParameter;
import com.sh.data.engine.domain.base.model.datax.DataX;
import com.sh.data.engine.domain.base.model.datax.DataxReaderAndWriter;
import com.sh.data.engine.domain.base.model.enums.XxlGroupEnum;
import com.sh.data.engine.domain.common.loader.CustomerUrlClassLoader;
import com.sh.data.engine.domain.common.service.DistributedService;
import com.sh.data.engine.domain.common.service.impl.CommandExecutorServiceImpl;
import com.sh.data.engine.domain.datadev.offline.model.enums.OfflineDevJobStatusEnum;
import com.sh.data.engine.domain.datadev.offline.model.enums.OfflineDevTaskStopEnum;
import com.sh.data.engine.domain.integration.api.record.model.domain.ApiRunningRecordDomain;
import com.sh.data.engine.domain.integration.api.record.service.ApiRunningRecordService;
import com.sh.data.engine.domain.integration.api.taskconfig.model.domain.*;
import com.sh.data.engine.domain.integration.api.taskconfig.model.param.ExpressionParam;
import com.sh.data.engine.domain.integration.api.taskconfig.model.param.PreviewExpressParam;
import com.sh.data.engine.domain.integration.api.taskconfig.model.param.TaskConfigParam;
import com.sh.data.engine.domain.integration.api.taskconfig.model.param.TaskRecordParam;
import com.sh.data.engine.domain.integration.api.taskconfig.service.TaskConfigService;
import com.sh.data.engine.domain.integration.api.webservice.model.domain.GetServiceDomain;
import com.sh.data.engine.domain.integration.api.webservice.model.domain.TaskDomain;
import com.sh.data.engine.domain.integration.api.webservice.model.domain.TaskOverviewDomain;
import com.sh.data.engine.domain.integration.api.webservice.model.domain.TestApiHttpDomain;
import com.sh.data.engine.domain.integration.api.webservice.model.mapper.TestAPiWsRequestMapper;
import com.sh.data.engine.domain.integration.api.webservice.model.mapper.WsApiAuthParamMapper;
import com.sh.data.engine.domain.integration.api.webservice.model.mapper.WsApiRequestBodyParamMapper;
import com.sh.data.engine.domain.integration.api.webservice.model.param.*;
import com.sh.data.engine.domain.integration.api.webservice.service.ApiWsService;
import com.sh.data.engine.domain.integration.datasource.model.domain.DataSourceDomain;
import com.sh.data.engine.domain.integration.offline.model.enums.OfflineSyncExecuTypeEnum;
import com.sh.data.engine.domain.integration.offline.model.mapper.OfflineSyncScheduleMapper;
import com.sh.data.engine.domain.integration.offline.service.DataXService;
import com.sh.data.engine.domain.shims.db.model.FieldInfoDomain;
import com.sh.data.engine.domain.shims.db.model.TableInfoDomain;
import com.sh.data.engine.domain.util.ConvertUtil;
import com.sh.data.engine.domain.workspace.manager.model.domain.UserDomain;
import com.sh.data.engine.domain.workspace.manager.service.UserService;
import com.sh.data.engine.job.admin.controller.JobInfoController;
import com.sh.data.engine.job.admin.core.model.XxlJobInfo;
import com.sh.data.engine.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.sh.data.engine.job.admin.core.scheduler.MisfireStrategyEnum;
import com.sh.data.engine.job.admin.core.scheduler.ScheduleTypeEnum;
import com.sh.data.engine.job.admin.service.XxlJobService;
import com.sh.data.engine.job.core.biz.model.ReturnT;
import com.sh.data.engine.job.core.enums.ExecutorBlockStrategyEnum;
import com.sh.data.engine.job.core.glue.GlueTypeEnum;
import com.sh.data.engine.repository.dao.base.EngineBaseModel;
import com.sh.data.engine.repository.dao.integration.api.http.entity.ApiResponseEntity;
import com.sh.data.engine.repository.dao.integration.api.http.entity.TaskConfigParamEntity;
import com.sh.data.engine.repository.dao.integration.api.record.entity.ApiRunningRecordEntity;
import com.sh.data.engine.repository.dao.integration.api.webservice.ApiWsMapper;
import com.sh.data.engine.repository.dao.integration.api.webservice.entity.ApiWsEntity;
import com.sh.data.engine.repository.dao.integration.api.webservice.entity.ApiWsEntity.ApiHttpRequestBodyParam;
import com.sh.data.engine.repository.dao.integration.api.webservice.entity.ApiWsEntity.ApiHttpRequestBodyParam.FormParam;
import com.sh.data.engine.repository.dao.integration.api.webservice.entity.ApiWsEntity.ApiWsRequest;
import com.sh.data.engine.repository.dao.integration.offline.entity.OfflineSyncEntity;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.dynamic.DynamicClientFactory;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.http.entity.ContentType;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

/**
 * @author : changkong
 * @date : 2022/3/11
 */
@Slf4j
@Service
public class ApiWsServiceImpl extends ServiceImpl<ApiWsMapper, ApiWsEntity>
    implements ApiWsService {

    @Autowired
    private TaskConfigService taskConfigServiceImpl;

    @Autowired
    private JobInfoController jobInfoController;

    @Autowired
    private XxlJobService xxlJobService;

    @Autowired
    private CommandExecutorServiceImpl commandExecutorService;

    @Autowired
    private ApiRunningRecordService apiRunningRecordService;

    @Autowired
    private DataXService dataXService;

    @Autowired
    private UserService userService;

    @Autowired
    private DistributedService distributedService;

    @Autowired
    private TestAPiWsRequestMapper testAPiWsRequestMapper;

    @Autowired
    private WsApiAuthParamMapper wsApiAuthParamMapper;

    @Autowired
    private WsApiRequestBodyParamMapper wsApiRequestBodyParamMapper;

    @Autowired
    private OfflineSyncScheduleMapper offlineSyncScheduleMapper;

    private Snowflake snowflake = new Snowflake();

    /**
     * ws接口测试
     *
     * @param testApiWsParam
     */
    @Override
    public TestApiHttpDomain testApi(TestApiWsParam testApiWsParam) {
        return execRequest(testApiWsParam);
    }

    /**
     * 上传jar包
     *
     * @param multipartFile
     * @return
     */
    @Override
    public JarUploadResultDomain uploadJar(MultipartFile multipartFile) {
        return taskConfigServiceImpl.uploadJar(multipartFile);
    }

    /**
     * 预览
     *
     * @param previewExpressParam
     * @return
     */
    @Override
    public List<List<Object>> preview(PreviewExpressParam previewExpressParam) {
        return taskConfigServiceImpl.preview(previewExpressParam);
    }

    /**
     * 获取数据源列表
     *
     * @param projectId
     * @return
     */
    @Override
    public List<DataSourceDomain> getDatabases(Long projectId) {
        return taskConfigServiceImpl.getDatabases(projectId);
    }

    /**
     * 在数据源中选择目标表
     *
     * @param dsId
     * @return
     */
    @Override
    public List<TableInfoDomain> getTargetTables(Long dsId) {
        return taskConfigServiceImpl.getTargetTables(dsId);
    }

    /**
     * 根据字段获取建表语句
     *
     * @param projectId
     * @param fields
     * @return
     */
    @Override
    public String getCreateTableDDL(
        Long projectId, Long dsId, List<String> fields, Integer isSupportRtf) {
        return taskConfigServiceImpl.getCreateTableDDL(projectId, dsId, fields, isSupportRtf);
    }

    /**
     * 执行建表语句
     *
     * @param projectId
     * @param sql
     */
    @Override
    public String saveTargetTable(Long projectId, String sql) {
        return taskConfigServiceImpl.saveTargetTable(projectId, sql);
    }

    @Override
    public String saveTargetTable(Long projectId, String sql, Long dataSecretId) {
        return taskConfigServiceImpl.saveTargetTable(projectId, sql, dataSecretId);
    }

    /**
     * 获取目标表字段信息
     *
     * @param dsId
     * @param tableName
     * @return
     */
    @Override
    public List<FieldInfoDomain> getTargetTableFields(Long dsId, String tableName) {
        return taskConfigServiceImpl.getTargetTableFields(dsId, tableName);
    }

    /**
     * datax任务脚本配置
     *
     * @param reader
     * @param task
     * @return
     */
    @Override
    public String buildJobJson(DataX.JobContentReader reader, TaskConfigParam task) {
        return taskConfigServiceImpl.buildJobJson(reader, task);
    }

    /**
     * 操作任务
     *
     * @param taskId
     * @param type
     */
//    @BizDataIndexAnnotation(
//        type = BizDataIndexTypeEnum.API_WS_COLLECT,
//        dataIdFieldName = "taskId",
//        dataIdParamIndex = 0,
//        operateType = BizDataIndexAnnotation.OperateType.DELETE)
    @Override
    public void operate(Long taskId, String type) {
        log.info("taskId:{},type:{}", taskId, type);
        ApiWsEntity task = this.getById(taskId);
        if (null == task || null == task.getXxlJobId()) {
            throw new BusinessException("检查任务状态");
        }
        Integer xxlJobId = task.getXxlJobId();
        switch (type) {
            case "online":
                task.setTaskStatus(ApiTaskStatus.released.getTaskStatus());
                task.setUpdateTime(new Date());
                task.setUpdaterId(UserContext.getUserId());
                task.setEndTime(new Date());
                this.update(task, new LambdaUpdateWrapper<ApiWsEntity>().eq(ApiWsEntity::getId, taskId));
                jobInfoController.start(xxlJobId);
                break;
            case "offline":
                task.setTaskStatus(ApiTaskStatus.torelease.getTaskStatus());
                task.setUpdateTime(new Date());
                task.setUpdaterId(UserContext.getUserId());
                task.setEndTime(null);
                this.update(task, new LambdaUpdateWrapper<ApiWsEntity>().eq(ApiWsEntity::getId, taskId));
                jobInfoController.stop(xxlJobId);
                break;
            default:
                throw new BusinessException("不支持的操作");
        }
    }

    @Override
    public PageResult<TaskRecordDomain> historyTask(TaskRecordParam taskRecordParam) {
        return null;
    }

    /**
     * 页面上方任务状态统计信息
     *
     * @param projectId
     * @return
     */
    @Override
    public TaskOverviewDomain getOverview(Long projectId) {
        QueryWrapper<ApiWsEntity> wrapper = new QueryWrapper<>();
        wrapper
            .select("task_status, count(1) as count")
            .eq("project_id", projectId)
            .eq("row_state", 1)
            .groupBy("task_status");
        List<Map<String, Object>> overViewMapList = this.listMaps(wrapper);
        long downlined = 0L, successed = 0L, failed = 0L;
        for (Map<String, Object> item : overViewMapList) {
            Object task_status = item.get("task_status");
            if (task_status == null) {
                task_status = item.get("TASK_STATUS");
            }
            int taskStatus = (int) task_status;
            Object count1 = item.get("count");
            if (count1 == null) {
                count1 = item.get("COUNT");
            }
            long count = (long) count1;
            ApiTaskStatus status = ApiTaskStatus.from(taskStatus);
            switch (status) {
                case torelease:
                    downlined = count;
                    break;
                case success:
                    successed = count;
                    break;
                case failure:
                    failed = count;
                    break;
                default:
                    break;
            }
        }
        return new TaskOverviewDomain(downlined, successed, failed);
    }

    /**
     * 任务列表分页
     *
     * @param pageNum
     * @param pageSize
     * @param search
     * @return
     */
    @Override
    public PageResult<TaskItemDomain> getTaskPage(
        Integer pageNum, Integer pageSize, Integer status, String search) {

        Long projectId = ServletUtils.getProjectId();

        Page<ApiWsEntity> page =
            this.page(
                new Page<ApiWsEntity>(pageNum, pageSize),
                new LambdaQueryWrapper<ApiWsEntity>()
                    .eq(ApiWsEntity::getProjectId, projectId)
                    .like(StringUtils.isNotBlank(search), ApiWsEntity::getTaskName, search)
                    .eq(ApiWsEntity::getRowState, 1));

        PageResult<TaskItemDomain> result =
            new PageResult<>(Lists.newArrayList(), pageNum, pageSize, 0);
        if (page == null || CollectionUtils.isEmpty(page.getRecords())) {
            return result;
        }
        result.setTotalElements(page.getTotal());
        result.setPageNum(pageNum);
        result.setPageSize(pageSize);
        result.setTotalPages(Math.toIntExact(page.getPages()));
        List<TaskItemDomain> resultDomainList = new ArrayList<>();
        List<String> userIdList =
            page.getRecords().stream().map(EngineBaseModel::getCreatorId).collect(Collectors.toList());
        Map<String, String> mapByUserIds = userService.getMapByUserIds(userIdList);

        for (ApiWsEntity api : page.getRecords()) {
            TaskItemDomain targetPageInfo = new TaskItemDomain();
            targetPageInfo.setId(api.getId());
            targetPageInfo.setTaskName(api.getTaskName());
            targetPageInfo.setUrl(api.getApiWsRequest().getAddress());
            TaskConfigParamEntity taskConfigParam = api.getTaskConfigParam();
            targetPageInfo.setStorageTable(api.getTaskConfigParam().getTargetTableName());
            targetPageInfo.setTaskStatus(api.getTaskStatus());
            targetPageInfo.setEffectiveDateStart(taskConfigParam.getEffectiveDateStart());
            targetPageInfo.setEffectiveDateEnd(taskConfigParam.getEffectiveDateEnd());
            targetPageInfo.setCycle(taskConfigParam.getCycle());
            targetPageInfo.setExecPoint(taskConfigParam.getExecPoint());
            targetPageInfo.setCycleDependence(taskConfigParam.getCycleDependence());
            // 转换成用户名
            String userName = mapByUserIds.getOrDefault(api.getCreatorId(), "");
            targetPageInfo.setCreateBy(userName);
            resultDomainList.add(targetPageInfo);
        }
        result.setResult(resultDomainList);
        return result;
    }

    /**
     * 保存任务
     *
     * @param id
     * @param taskName
     * @param testApiWsParam
     * @param previewParam
     * @param task
     */
//    @BizDataIndexAnnotation(
//        type = BizDataIndexTypeEnum.API_WS_COLLECT,
//        dataIdFieldName = "id",
//        dataIdParamIndex = 0,
//        operateType = BizDataIndexAnnotation.OperateType.SAVE)
    @Override
    public void save(
        Long id,
        String taskName,
        TestApiWsParam testApiWsParam,
        PreviewExpressParam previewParam,
        TaskConfigParam task) {

        Long projectId = ServletUtils.getProjectId();
        ApiWsEntity one =
            this.getOne(
                new LambdaQueryWrapper<ApiWsEntity>()
                    .eq(ApiWsEntity::getProjectId, projectId)
                    .eq(ApiWsEntity::getTaskName, taskName)
                    .eq(ApiWsEntity::getRowState, 1));

        Long existId = null != one ? one.getId() : null;

        // datax json任务
        Long targetDsId = task.getTargetDsId();
        String originTableName = task.getTargetTableName();
        //    String dataXTableName = TaskConfigServiceImpl.dataXTaskAddSchema(task,
        // dataSourceDetailById);
        //    task.setTargetTableName(dataXTableName);
        // datax json任务
//    DataX.JobContentReader jobContentReader = buildJobReader(testApiWsParam, previewParam, task);
//    String jobJsonString = this.buildJobJson(jobContentReader, task);
        // 构建完任务后把schema去掉
        task.setTargetTableName(originTableName);
        ApiWsEntity apiWsEntity =
            buildApiWsEntity(taskName, testApiWsParam, previewParam, task, null);

        if (null == id) {
            if (null != existId) {
                throw new BusinessException("任务名称已存在");
            }
            apiWsEntity.setTaskStatus(ApiTaskStatus.torelease.getTaskStatus());
            this.save(apiWsEntity);
            // id给数据地图用
            id = apiWsEntity.getId();
        } else {
            if ((null != existId) && (!Objects.equals(existId, id))) {
                throw new BusinessException("任务名称已存在");
            }
            apiWsEntity.setId(id);
            this.updateById(apiWsEntity);
            if (null != apiWsEntity.getXxlJobId()) {
                log.info("task更新,移除绑定的xxljob:{}", apiWsEntity.getXxlJobId());
                xxlJobService.remove(apiWsEntity.getXxlJobId());
            }
            // xxl job update不生效, 清空xxlJobId重新生成
            apiWsEntity.setXxlJobId(null);
        }
        // 添加到xxlJob
        Integer xxlJobId;
        try {
            xxlJobId = generateJobId(apiWsEntity, task);
        } catch (Exception e) {
            log.error("创建xxl任务失败", e.getMessage());
            throw new BusinessException("检查调度平台状态");
        }
        apiWsEntity.setXxlJobId(xxlJobId);
        this.updateById(apiWsEntity);
    }

    /**
     * 保存xxl任务生成任务id
     *
     * @param apiWsEntity
     * @param task
     * @return
     */
    @Override
    public Integer generateJobId(ApiWsEntity apiWsEntity, TaskConfigParam task) {
        // 重用离线同步的调度配置
        OfflineSyncEntity.OfflineSyncSchedule schedule = offlineSyncScheduleMapper.map(task);
        if (schedule == null) {
            return null;
        }

        int triggerStatus = 0;
        // 工作流未锁定且配置了调度,未发布
        if (Objects.nonNull(schedule.getStatus()) && schedule.getStatus().equals(0)) {
            triggerStatus = 1;
        }

        boolean isInsert = true;
        XxlJobInfo jobInfo = new XxlJobInfo();
        if (Objects.nonNull(apiWsEntity.getXxlJobId())) {
            jobInfo.setId(apiWsEntity.getXxlJobId());
            isInsert = false;
        }

        jobInfo.setJobGroup(XxlGroupEnum.DATA_ENGINE_SYSTEMTASK_GROUP.getId());

        jobInfo.setJobDesc(apiWsEntity.getTaskName());

        final UserDomain userInfo = UserContext.getUserInfo();
        jobInfo.setAuthor(null == userInfo ? "" : userInfo.getUserName());
        jobInfo.setAlarmEmail(null == userInfo ? "" : userInfo.getEmail());
        jobInfo.setAddTime(new Date());

        jobInfo.setScheduleType(ScheduleTypeEnum.HUFU.name());

        // 设置未冻结
        schedule.setStatus(0);

        jobInfo.setScheduleConf(JSON.toJSONString(schedule));

        jobInfo.setMisfireStrategy(MisfireStrategyEnum.DO_NOTHING.name()); // 调度过期策略

        jobInfo.setExecutorRouteStrategy(ExecutorRouteStrategyEnum.ROUND.name());

        jobInfo.setExecutorHandler("OfflineApiWsHandler");

        jobInfo.setExecutorBlockStrategy(ExecutorBlockStrategyEnum.DISCARD_LATER.name());

        jobInfo.setGlueType(GlueTypeEnum.BEAN.name());

        jobInfo.setTriggerStatus(triggerStatus); // 默认是停止状态

        jobInfo.setEffectFrom(schedule.getEffectiveDateStart());

        jobInfo.setEffectTo(schedule.getEffectiveDateEnd());

        if (isInsert) {
            ReturnT<String> add = xxlJobService.add(jobInfo);

            return Integer.parseInt(add.getContent());
        }
        ReturnT<String> add = xxlJobService.update(jobInfo);

        return Integer.parseInt(add.getContent());
    }

    /**
     * 获取任务详情
     *
     * @param taskId
     * @return
     */
    @Override
    public TaskDomain getTaskById(Long taskId) {
        ApiWsEntity apiWsEntity = this.getById(taskId);
        TaskDomain taskDomain = new TaskDomain();
        taskDomain.setId(apiWsEntity.getId());
        taskDomain.setTaskName(apiWsEntity.getTaskName());
        taskDomain.setTaskStatus(apiWsEntity.getTaskStatus());
        taskDomain.setDataxJobScript(apiWsEntity.getDataxJobScript());
        // 请求部分
        ApiWsEntity.ApiWsRequest sourceApiHttpRequest = apiWsEntity.getApiWsRequest();
        if (null != sourceApiHttpRequest) {
            TaskDomain.TestAPiWsRequest testAPiHttpRequest = testAPiWsRequestMapper.map(sourceApiHttpRequest);
            testAPiHttpRequest.setApiWsAuthParam(
                wsApiAuthParamMapper.map(sourceApiHttpRequest.getApiWsAuthParam()));
//                ConvertUtil.copyProperties(
//                    sourceApiHttpRequest.getApiWsAuthParam(),
//                    TaskDomain.TestAPiWsRequest.ApiHttpAuthParam.class));
            //      testAPiHttpRequest.setApiHttpRequestHeaderParam(
            //          ConvertUtil.copyProperties(
            //              sourceApiHttpRequest.getApiHttpRequestHeaderParam(),
            //              TaskDomain.TestAPiHttpRequest.ApiHttpRequestHeaderParam.class));
            testAPiHttpRequest.setApiWsRequestBodyParam(
                wsApiRequestBodyParamMapper.map(sourceApiHttpRequest.getApiWsRequestBodyParam()));
//                ConvertUtil.copyProperties(
//                    sourceApiHttpRequest.getApiWsRequestBodyParam(),
//                    TaskDomain.TestAPiWsRequest.ApiHttpRequestBodyParam.class));

            taskDomain.setApiWsRequest(testAPiHttpRequest);
        }

        // 返回值处理
        ApiResponseEntity sourceApiHttpResponse = apiWsEntity.getApiResponse();
        if (null != sourceApiHttpResponse) {
            TaskDomain.PreviewExpressRequest previewExpressRequest =
                ConvertUtil.copyProperties(sourceApiHttpResponse, TaskDomain.PreviewExpressRequest.class);

            if (CollectionUtils.isNotEmpty(sourceApiHttpResponse.getExpressionParamList())) {
                previewExpressRequest.setExpressionParamList(
                    ConvertUtil.copyProperties(
                        sourceApiHttpResponse.getExpressionParamList(),
                        TaskDomain.PreviewExpressRequest.ExpressionParam.class));

                taskDomain.setPreviewExpressRequest(previewExpressRequest);
            }
        }
        TaskConfigParamEntity taskConfigParam = apiWsEntity.getTaskConfigParam();
        // 任务配置相关
        TaskConfigDomain taskConfigRequest =
            ConvertUtil.copyProperties(taskConfigParam, TaskConfigDomain.class);

        taskConfigRequest.setTargetFields(
            ConvertUtil.copyProperties(
                taskConfigParam.getTargetFields(), TaskConfigDomain.TargetField.class));
        taskConfigRequest.setFieldMappings(
            ConvertUtil.copyProperties(
                taskConfigParam.getFieldMappings(), TaskConfigDomain.FieldMapping.class));
        taskConfigRequest.setPartitions(
            ConvertUtil.copyProperties(
                taskConfigParam.getPartitions(), TaskConfigDomain.PartitionField.class));
        taskDomain.setTaskConfigRequest(taskConfigRequest);
        return taskDomain;
    }

    /**
     * webservice获取service下port,operation
     *
     * @return
     */
    @Override
    public GetServiceDomain getWsdlPortsAndMethods(GetServiceParam getServiceParam) {
        String wsdlClassPath = "/data/dev/wsdl/" + snowflake.nextIdStr();
        File wsdlClassFilePath = new File(wsdlClassPath);
        if (wsdlClassFilePath.exists()) {
            if (wsdlClassFilePath.isDirectory()) {
                File[] files = wsdlClassFilePath.listFiles();
                if (null != files && files.length != 0) {
                    log.info("清空{}目录下文件", wsdlClassFilePath);
                    CommandExecutorServiceImpl.ExecuteResult
                        deletePath = commandExecutorService.executeCommand("rm -rf " + wsdlClassPath);
                    if (!deletePath.isSuccess()) {
                        throw new BusinessException("清理目录,解析wsdl前置步骤失败");
                    }
                }
            } else {
                // 不是文件夹,尝试删除
                log.info("清理{}异常文件", wsdlClassFilePath);
                CommandExecutorServiceImpl.ExecuteResult deletePath =
                    commandExecutorService.executeCommand("rm -rf " + wsdlClassPath);
                if (!deletePath.isSuccess()) {
                    throw new BusinessException("清理异常文件,解析wsdl前置步骤失败");
                }
            }
        }
        String createWsdlClassPathCmd = "mkdir -p " + wsdlClassPath;
        CommandExecutorServiceImpl.ExecuteResult createPathResult =
            commandExecutorService.executeCommand(createWsdlClassPathCmd);
        if (!createPathResult.isSuccess()) {
            throw new BusinessException("创建目录,解析wsdl前置步骤失败");
        }
        String address = getServiceParam.getAddress();
        String authNameKey = getServiceParam.getAddressUserNameKey();
        String authNameVal = getServiceParam.getAddressUserNameValue();
        String authPwdKey = getServiceParam.getAddressPasswordKey();
        String authPwdVal = getServiceParam.getAddressPasswordValue();
        // 生成class文件
        if (StringUtils.isNotBlank(authNameKey) && StringUtils.isNotBlank(authPwdKey)) {
            String queryParamterJoinKey = getQueryParamterJoinKey(address);
            address =
                String.format(
                    "%s%s%s=%s&%s=%s",
                    address, queryParamterJoinKey, authNameKey, authNameVal, authPwdKey, authPwdVal);
        }
        String wsimportFormat = "wsdl2java  -bareMethods -encoding utf-8 -compile -d %s  '%s'";
        String wsimportCmd = String.format(wsimportFormat, wsdlClassPath, address);
        CommandExecutorServiceImpl.ExecuteResult wsimportResult =
            commandExecutorService.executeCommand(wsimportCmd);
        if (!wsimportResult.isSuccess()) {
            throw new BusinessException("wsdl地址解析失败");
        }

        final Set<Class<?>> classes;
        CustomerUrlClassLoader classLoader = new CustomerUrlClassLoader();
        try {
            classes = loadClasses(wsdlClassPath, classLoader);
            Map<String, List<String>> servicePorts = getServicePorts(classes);
            List<String> serviceList = servicePorts.get("service");
            log.info("serviceList:" + serviceList);
            List<String> portList = servicePorts.get("port");
            if (org.apache.commons.collections.CollectionUtils.isEmpty(serviceList)
                || org.apache.commons.collections.CollectionUtils.isEmpty(portList)) {
                throw new BusinessException("wsdl文件中没有找到服务和端口");
            }
            // 要返回给前端的ports
            log.info("portList:" + portList);
            // 通过选定的port,获取下面对应的所有方法,返回给前端
            Map<String, List<String>> resultMap = new HashMap<>();
            for (String port : portList) {
                List<String> operationsByPort = getOperationsByPort(classes, serviceList.get(0));
                resultMap.put(port, operationsByPort);
            }
            GetServiceDomain getServiceDomain = new GetServiceDomain();
            getServiceDomain.setService(serviceList.get(0));
            getServiceDomain.setPorts(resultMap.keySet());
            getServiceDomain.setPortOperationMap(resultMap);
            getServiceDomain.setLocalClassPath(wsdlClassPath);
            return getServiceDomain;
        } catch (Exception e) {
            log.error(
                "localClassPath: {} url:{} 获取wsdl接口信息失败: {}", wsdlClassPath, address,
                e.getMessage());
            throw new BusinessException("获取wsdl接口信息失败");
        }
    }

    @Override
    public Long getApiIdByXxlId(long xxlId) {
        return Optional.ofNullable(
                this.getOne(new LambdaQueryWrapper<ApiWsEntity>().eq(ApiWsEntity::getXxlJobId, xxlId)))
            .orElseThrow(() -> new BusinessException(String.format("未找到JobId为%d的api", xxlId)))
            .getId();
    }

    /**
     * 参数转换到entity
     *
     * @param taskName
     * @param testApiWsParam
     * @param previewParam
     * @param task
     * @param jobJsonString
     * @return
     */
    @NotNull
    private ApiWsEntity buildApiWsEntity(
        String taskName,
        TestApiWsParam testApiWsParam,
        PreviewExpressParam previewParam,
        TaskConfigParam task,
        String jobJsonString) {
        Long projectId = ServletUtils.getProjectId();

        ApiWsEntity apiWsEntity = new ApiWsEntity();
        apiWsEntity.setProjectId(projectId);
        apiWsEntity.setTaskName(taskName);
        // http请求参数配置
        ApiWsEntity.ApiWsRequest apiWsRequest =
            ConvertUtil.copyProperties(testApiWsParam, ApiWsEntity.ApiWsRequest.class);

        ApiWsEntity.ApiHttpAuthParam apiWsAuthParam =
            ConvertUtil.copyProperties(
                testApiWsParam.getApiWsAuthParam(), ApiWsEntity.ApiHttpAuthParam.class);
        apiWsRequest.setApiWsAuthParam(apiWsAuthParam);

        // 如果有设置请求头, 复制请求头
        //    if (null != testApiWsParam.getApiHttpRequestHeaderParam()
        //        &&
        // CollectionUtils.isNotEmpty(testApiWsParam.getApiHttpRequestHeaderParam().getParams())) {
        //      apiWsEntity.ApiHttpRequestHeaderParam apiHttpRequestHeaderParam =
        //          new apiWsEntity.ApiHttpRequestHeaderParam(
        //              ConvertUtil.copyProperties(
        //                  testApiWsParam.getApiHttpRequestHeaderParam().getParams(),
        //                  apiWsEntity.ApiHttpRequestHeaderParam.Header.class));
        //      apiHttpRequest.setApiHttpRequestHeaderParam(apiHttpRequestHeaderParam);
        //    }

        // 如果content_type设置了x-www-form-urlencoded,复制请求体form参数
        ApiWsEntity.ApiHttpRequestBodyParam apiWsRequestBodyParam =
            ConvertUtil.copyProperties(
                testApiWsParam.getApiWsRequestBodyParam(), ApiWsEntity.ApiHttpRequestBodyParam.class);

        if (null != testApiWsParam.getApiWsRequestBodyParam()
            && Objects.equals(
            "application/x-www-form-urlencoded",
            testApiWsParam.getApiWsRequestBodyParam().getContentType())) {
            apiWsRequest
                .getApiWsRequestBodyParam()
                .setFormParams(
                    ConvertUtil.copyProperties(
                        testApiWsParam.getApiWsRequestBodyParam().getFormParams(),
                        ApiWsEntity.ApiHttpRequestBodyParam.FormParam.class));
        }
        apiWsRequest.setApiWsRequestBodyParam(apiWsRequestBodyParam);

        apiWsEntity.setApiWsRequest(apiWsRequest);
        // 返回值解析
        ApiResponseEntity apiHttpResponse =
            ConvertUtil.copyProperties(previewParam, ApiResponseEntity.class);
        if (CollectionUtils.isNotEmpty(previewParam.getExpressionParamList())) {
            List<ApiResponseEntity.ExpressionParam> expressionParams =
                ConvertUtil.copyProperties(
                    previewParam.getExpressionParamList(), ApiResponseEntity.ExpressionParam.class);
            apiHttpResponse.setExpressionParamList(expressionParams);
        }

        apiWsEntity.setApiResponse(apiHttpResponse);

        // 任务数据目标,调度配置
        TaskConfigParamEntity taskConfigParam =
            ConvertUtil.copyProperties(task, TaskConfigParamEntity.class);
        taskConfigParam.setTargetFields(
            ConvertUtil.copyProperties(
                task.getTargetFields(), TaskConfigParamEntity.TargetField.class));
        taskConfigParam.setFieldMappings(
            ConvertUtil.copyProperties(
                task.getFieldMappings(), TaskConfigParamEntity.FieldMapping.class));
        taskConfigParam.setPartitions(
            ConvertUtil.copyProperties(
                task.getPartitions(), TaskConfigParamEntity.PartitionField.class));
        apiWsEntity.setTaskConfigParam(taskConfigParam);

        apiWsEntity.setDataxJobScript(jobJsonString);
        // 设置成未发布状态
        apiWsEntity.setTaskStatus(ApiTaskStatus.torelease.getTaskStatus());
        apiWsEntity.setRowState(1);
        apiWsEntity.setCreateTime(new Date());
        apiWsEntity.setCreatorId(UserContext.getUserId());
        apiWsEntity.setUpdateTime(new Date());
        apiWsEntity.setUpdaterId(UserContext.getUserId());
        return apiWsEntity;
    }

    public static DataX.JobContentReader buildJobReader(
        TestApiWsParam testApiWsParam, PreviewExpressParam previewParam, TaskConfigParam task) {
        DataX.JobContentReader reader = new DataX.JobContentReader();
        reader.setName(DataxReaderAndWriter.getReaderByName("WebService"));

        ApiWsJobContentReaderParameter readerParameter = new ApiWsJobContentReaderParameter();

        String address = testApiWsParam.getAddress();
        String authNameKey = testApiWsParam.getAddressUserNameKey();
        String authNameVal = testApiWsParam.getAddressUserNameValue();
        String authPwdKey = testApiWsParam.getAddressPasswordKey();
        String authPwdVal = testApiWsParam.getAddressPasswordValue();
        // 生成class文件
        if (StringUtils.isNotBlank(authNameKey) && StringUtils.isNotBlank(authPwdKey)) {
            String queryParamterJoinKey = getQueryParamterJoinKey(address);
            address =
                String.format(
                    "%s%s%s=%s&%s=%s",
                    address, queryParamterJoinKey, authNameKey, authNameVal, authPwdKey, authPwdVal);
        }
        readerParameter.setWsdlUrl(address);
        readerParameter.setPort(testApiWsParam.getPort());
        readerParameter.setOperation(testApiWsParam.getOperation());
        readerParameter.setLocalClassPath(testApiWsParam.getLocalClassPath());
        ApiWsRequestBodyParam apiWsRequestBodyParam = testApiWsParam.getApiWsRequestBodyParam();

        String contentType = apiWsRequestBodyParam.getContentType();
        readerParameter.setContentType(Objects.equals(contentType, "application/json") ? 1 : 0);
        readerParameter.setRaw(apiWsRequestBodyParam.getBody());
        readerParameter.setJsScript(previewParam.getJsScript());
        readerParameter.setAdvancedFilePath(testApiWsParam.getApiWsAuthParam().getAdvancedFilePath());
        readerParameter.setAdvancedClassReference(
            testApiWsParam.getApiWsAuthParam().getAdvanceClassReference());
        readerParameter.setAdvancedMethodName(
            testApiWsParam.getApiWsAuthParam().getAdvanceMethodName());

        List<ApiWsJobContentReaderParameter.RequestParam> requestParams = Lists.newArrayList();
        List<ApiWsJobContentReaderParameter.ResponseParam> responseParams = Lists.newArrayList();

        List<String> fields = Lists.newArrayList();
        task.getFieldMappings()
            .forEach(
                i -> {
                    String sourceField = i.getSourceField();
                    fields.add(sourceField);
                });

        // 表单参数处理
        if (readerParameter.getContentType() == 0
            && CollectionUtils.isNotEmpty(apiWsRequestBodyParam.getFormParams())) {
            for (ApiWsRequestBodyParam.FormParam param : apiWsRequestBodyParam.getFormParams()) {
                Integer paramType = param.getParamType();
                if (paramType == 0) {
                    ApiWsJobContentReaderParameter.RequestParam requestParam =
                        new ApiWsJobContentReaderParameter.RequestParam();
                    requestParam.setName(param.getParamName());
                    requestParam.setPosition(param.getParamPosition());

                    Integer paramAttribute = param.getParamAttribute();
                    String type = null;
                    if (paramAttribute == 1) {
                        type = "pageNo";
                    } else if (paramAttribute == 2) {
                        type = "pageSize";
                    }
                    requestParam.setType(type);
                    // todo 系统参数值替换
                    requestParam.setValue(param.getParamValue());
                    requestParams.add(requestParam);
                }
            }
        }
        // 返回参数处理
        List<ExpressionParam> expressionParamList = previewParam.getExpressionParamList();
        Map<String, ExpressionParam> fieldExpressionMap =
            expressionParamList.stream().collect(Collectors.toMap(ExpressionParam::getName, i -> i));

        // 用户直接建表, 维护表字段顺序匹配
        for (String sourceField : fields) {
            ExpressionParam expressionParam = fieldExpressionMap.get(sourceField);
            if (null == expressionParam) {
                continue;
            }
            ApiWsJobContentReaderParameter.ResponseParam responseParam =
                new ApiWsJobContentReaderParameter.ResponseParam();
            responseParam.setName(expressionParam.getName());
            responseParam.setExpression(expressionParam.getExpression());
            responseParam.setRelationship(expressionParam.getRelationship());
            responseParams.add(responseParam);
        }

        readerParameter.setRequestParams(requestParams);
        readerParameter.setResponseParams(responseParams);

        reader.setParameter(readerParameter);
        return reader;
    }

    /**
     * 发送请求
     *
     * @return
     */
    @NotNull
    private TestApiHttpDomain execRequest(TestApiWsParam testApiWsParam) {
        URL url = null;
        try {
            String address = testApiWsParam.getAddress();
            String authNameKey = testApiWsParam.getAddressUserNameKey();
            String authNameVal = testApiWsParam.getAddressUserNameValue();
            String authPwdKey = testApiWsParam.getAddressPasswordKey();
            String authPwdVal = testApiWsParam.getAddressPasswordValue();
            // 生成class文件
            if (StringUtils.isNotBlank(authNameKey) && StringUtils.isNotBlank(authPwdKey)) {
                String queryParamterJoinKey = getQueryParamterJoinKey(address);
                address =
                    String.format(
                        "%s%s%s=%s&%s=%s",
                        address, queryParamterJoinKey, authNameKey, authNameVal, authPwdKey, authPwdVal);
            }
            url = new URL(address);
        } catch (MalformedURLException e) {
            throw new BusinessException("wsdl格式错误");
        }
        Set<Class<?>> classes = null;
        CustomerUrlClassLoader classLoader = new CustomerUrlClassLoader();
        try {
            classes = loadClasses(testApiWsParam.getLocalClassPath(), classLoader);
        } catch (Exception e) {
            throw new BusinessException("class路径错误");
        }
        // 根据operation获取入参和出参
        Pair<Class, Class> paramersPair = null;
        try {
            paramersPair =
                getOperationParamers(classes, testApiWsParam.getPort(), testApiWsParam.getOperation());
        } catch (Exception e) {
            throw new BusinessException("class获取参数错误");
        }
        Class inParameterClass = paramersPair.getFirst();

        // JSON 转 对象
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        Object convertObject = null;
        try {
            convertObject =
                objectMapper.readValue(
                    testApiWsParam.getApiWsRequestBodyParam().getBody(), inParameterClass);
        } catch (IOException e) {
            log.error("入参转换异常: {}", e.getMessage());
            throw new BusinessException("检查接口参数是否输入正确");
        }
        // cxf client创建
        Client client =
            Objects.equals("jax-ws", testApiWsParam.getProtocol())
                ? JaxWsDynamicClientFactory.newInstance().createClient(url, classLoader)
                : DynamicClientFactory.newInstance().createClient(url, classLoader);
        try {
            Object[] results = client.invokeWrapped(testApiWsParam.getOperation(), convertObject);
            String jsonString = "";
            // 如果返回结果是多元素数组, 组装成json string
            if (results.length == 1) {
                jsonString = objectMapper.writeValueAsString(results[0]);
            } else {
                Class responseObjectClass = paramersPair.getSecond();
                Field[] declaredFields = responseObjectClass.getDeclaredFields();
                JSONObject jsonObject = new JSONObject();
                for (int i = 0; i < results.length; i++) {
                    jsonObject.put(declaredFields[i].getName(), results[i]);
                }
                jsonString = jsonObject.toJSONString();
            }
            log.info("返回结果: " + jsonString);
            TestApiHttpDomain testResponse = new TestApiHttpDomain();
            testResponse.setResponseBody(jsonString);
            testResponse.setIsSuccessful(true);
            testResponse.setCode(200);

            // 数据预览时填写的提示参数
            List<String> jsonPaths = JsonPathUtil.getJsonPaths(jsonString);
            testResponse.setExpressions(jsonPaths);
            return testResponse;
        } catch (JsonProcessingException e) {
            log.error(e.getMessage());
            throw new BusinessException("请求结果json转换错误");
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new BusinessException("请求发生错误");
        } finally {
            try {
                if (null != client) {
                    client.close();
                }
            } catch (Exception e) {
                log.error("cxf client close异常{}", e.getMessage());
            }
        }
    }

    /**
     * 构造请求体
     *
     * @param url
     * @param httpRequestMethod
     * @param apiHttpRequestHeaderParam
     * @param contentType
     * @param formParams
     * @param rowJson
     * @return
     */
    @NotNull
    private Request buildRequest(
        String url,
        String httpRequestMethod,
        ApiWsAuthParam apiWsAuthParam,
        ApiHttpRequestHeaderParam apiHttpRequestHeaderParam,
        String contentType,
        List<ApiWsRequestBodyParam.FormParam> formParams,
        String rowJson) {
        Request.Builder builder = new Request.Builder().url(url);
        if (StringUtils.equalsIgnoreCase(httpRequestMethod, "GET")) {
            builder.get();
        } else {
            if (Objects.equals(ContentType.APPLICATION_JSON.getMimeType(), contentType)) {
                RequestBody jsonBodyParam = this.getJsonBodyParam(rowJson);
                builder.method(httpRequestMethod, jsonBodyParam);
            } else {
                RequestBody formBodyParam = this.getFormBodyParam(formParams);
                builder.method(httpRequestMethod, formBodyParam);
            }
        }
        Headers headers = getHeaders(apiHttpRequestHeaderParam);
        Headers.Builder headerBuilder = headers.newBuilder();
        // auth参数写入header
        switch (apiWsAuthParam.getAuthType()) {
            case "basic":
                String basicAuth =
                    String.format(
                        "%s:%s", apiWsAuthParam.getAuthUsername(), apiWsAuthParam.getAuthPassword());
                headerBuilder
                    .add(
                        "Authorization",
                        "Basic "
                            + Base64.getEncoder()
                            .encodeToString(basicAuth.getBytes(StandardCharsets.UTF_8)))
                    .build();
                break;
            case "sign":
                headerBuilder.add(apiWsAuthParam.getAccessKey(), apiWsAuthParam.getAccessSecret()).build();
                break;
            case "token":
                headerBuilder.add("Authorization", "Bearer " + apiWsAuthParam.getAccessToken()).build();
                break;
            default:
        }
        headers = headerBuilder.build();
        builder.headers(headers);
        Request req = builder.build();
        return req;
    }

    /**
     * 获取post请求参数
     *
     * @return
     */
    private RequestBody getFormBodyParam(List<ApiWsRequestBodyParam.FormParam> requestParams) {
        FormBody.Builder builder = new FormBody.Builder();
        if (CollectionUtils.isEmpty(requestParams)) {
            return builder.build();
        }
        for (ApiWsRequestBodyParam.FormParam param : requestParams) {
            String paramName = param.getParamName();

            String val = param.getParamValue();
            log.info("form param: {}={}", paramName, val);
            if (null != val) {
                builder.add(paramName, val);
            }
        }

        FormBody formBody = builder.build();

        return formBody;
    }

    /**
     * raw json
     *
     * @param json
     * @return
     */
    private RequestBody getJsonBodyParam(String json) {
        log.info("body json:{}", json);
        if (null == json) {
            json = "";
        }

        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");

        RequestBody requestBody = RequestBody.create(mediaType, json);

        return requestBody;
    }

    /**
     * header param
     *
     * @param param
     * @return
     */
    private Headers getHeaders(ApiHttpRequestHeaderParam param) {
        if (CollectionUtils.isEmpty(param.getParams())) {
            return Headers.of();
        }
        HashMap<String, String> headersMap = new HashMap<>();
        for (ApiHttpRequestHeaderParam.Header headerParam : param.getParams()) {
            headersMap.put(headerParam.getName(), headerParam.getValue());
        }
        return Headers.of(headersMap);
    }

    private Map<String, String> execAdvanced(
        String advancedFilePath, String advancedClassReference, String advancedMethodName)
        throws Exception {
        Map<String, String> resultMap = new HashMap<>();

        if (StringUtils.isBlank(advancedFilePath)
            || StringUtils.isBlank(advancedClassReference)
            || StringUtils.isBlank(advancedMethodName)) {
            log.info("advanced config will not be performed");
            return resultMap;
        }

        URL[] urls = new URL[]{};

        CustomerUrlClassLoader classLoader = new CustomerUrlClassLoader(urls);

        classLoader.addJar(new File(advancedFilePath).toURI().toURL());

        Class<?> clazz = classLoader.loadClass(advancedClassReference);

        Object instance = clazz.newInstance();

        Object returnValue = MethodUtils.invokeMethod(instance, advancedMethodName);

        // 清除相关引用供gc自动回收卸载Class
        instance = null;
        clazz = null;
        classLoader = null;

        if (null == returnValue || (!(returnValue instanceof HashMap))) {
            throw new BusinessException(
                "advanced config performed failed,the return is null or the return is not instanceof HashMap");
        }

        resultMap = (Map<String, String>) returnValue;

        return resultMap;
    }

    /**
     * 加载wsdl2java 解析出的文件
     *
     * @param rootClassPath
     * @param classLoader
     * @return
     * @throws Exception
     */
    private static Set<Class<?>> loadClasses(String rootClassPath, CustomerUrlClassLoader classLoader)
        throws Exception {
        Set<Class<?>> classSet = Sets.newHashSet();
        // 设置class文件所在根路径
        File clazzPath = new File(rootClassPath);

        // 记录加载.class文件的数量
        int clazzCount = 0;

        if (clazzPath.exists() && clazzPath.isDirectory()) {
            // 获取路径长度
            int clazzPathLen = clazzPath.getAbsolutePath().length() + 1;

            Stack<File> stack = new Stack<>();
            stack.push(clazzPath);

            // 遍历类路径
            while (!stack.isEmpty()) {
                File path = stack.pop();
                File[] classFiles =
                    path.listFiles(
                        new FileFilter() {
                            @Override
                            public boolean accept(File pathname) {
                                // 只加载class文件
                                return pathname.isDirectory() || pathname.getName().endsWith(".class");
                            }
                        });
                if (classFiles == null) {
                    break;
                }
                for (File subFile : classFiles) {
                    if (subFile.isDirectory()) {
                        stack.push(subFile);
                    } else {
                        if (clazzCount++ == 0) {
                            // 将当前类路径加入到类加载器中
                            classLoader.addClass(clazzPath.toURI().toURL());
                        }
                        // 文件名称
                        String className = subFile.getAbsolutePath();
                        className = className.substring(clazzPathLen, className.length() - 6);
                        // 将/替换成. 得到全路径类名
                        className = className.replace(File.separatorChar, '.');
                        // 加载Class类
                        Class<?> aClass = Class.forName(className, false, classLoader);
                        classSet.add(aClass);
                        log.info("读取应用程序类文件[class={" + className + "}]");
                    }
                }
            }
        }
        return classSet;
    }

    /**
     * class文件中找到service
     *
     * @param classes
     * @return
     */
    private static Map<String, List<String>> getServicePorts(Set<Class<?>> classes) {
        Map<String, List<String>> classMap = new HashMap<>();
        for (Class<?> aClass : classes) {
            final WebServiceClient annotation = aClass.getAnnotation(WebServiceClient.class);
            final WebService webServiceAnnotation = aClass.getAnnotation(WebService.class);
            if (null != annotation) {
                List<String> serviceClassList = classMap.getOrDefault("service", new ArrayList<>());
                serviceClassList.add(aClass.getSimpleName());
                classMap.putIfAbsent("service", serviceClassList);
            } else if (null != webServiceAnnotation) {
                List<String> portClassList = classMap.getOrDefault("port", new ArrayList<>());
                portClassList.add(aClass.getSimpleName());
                classMap.putIfAbsent("port", portClassList);
            }
        }
        return classMap;
    }

    /**
     * 获取port下所有操作
     *
     * @param classes
     * @param port
     * @return
     * @throws ClassNotFoundException
     */
    private static List<String> getOperationsByPort(Set<Class<?>> classes, String port)
        throws ClassNotFoundException {
        List<String> operationNames = new ArrayList<>();

        Class<?> portClass = null;

        for (Class<?> aClass : classes) {
            if (aClass.getSimpleName().equals(port)) {
                portClass = aClass;
                break;
            }
        }

        final Method[] methods = portClass.getMethods();

        for (Method method : methods) {
            final WebEndpoint annotation = method.getAnnotation(WebEndpoint.class);
            final int parameterCount = method.getParameterCount();
            if (null != annotation && parameterCount == 0) {
                final Class<?> returnType = method.getReturnType();
                final Method[] returnTypeMethods = returnType.getMethods();
                for (Method returnTypeMethod : returnTypeMethods) {
                    WebMethod webMethod = returnTypeMethod.getAnnotation(WebMethod.class);
                    if (null != webMethod) {
                        if (StringUtils.isEmpty(webMethod.operationName())) {
                            operationNames.add(returnTypeMethod.getName());
                        } else {
                            operationNames.add(webMethod.operationName());
                        }
                    }
                }
            }
        }
        return operationNames;
    }

    /**
     * 返回具体方法的入参和出参
     *
     * @param classes
     * @param port
     * @param operation
     * @return
     * @throws Exception
     */
    private static Pair<Class, Class> getOperationParamers(
        Set<Class<?>> classes, String port, String operation) throws Exception {

        Class<?> portClass = null;

        for (Class<?> aClass : classes) {
            if (aClass.getSimpleName().equals(port)) {
                portClass = aClass;
                break;
            }
        }
        final Method[] methods = portClass.getMethods();
        for (Method method : methods) {
            WebMethod webMethod = method.getAnnotation(WebMethod.class);
            if (webMethod == null) {
                continue;
            }
            String operationName = StringUtils.isEmpty(webMethod.operationName()) ? method.getName() : webMethod.operationName();
            if (operationName.equals(operation)) {
                Parameter[] parameters = method.getParameters();
                if (parameters.length != 1) {
                    System.out.println("检查WebService接口文件参数");
                    return null;
                }
                Parameter parameter = parameters[0];
                Class<?> type = parameter.getType();
                Class<?> returnType = method.getReturnType();
                return Pair.of(type, returnType);
            }
        }
        return null;
    }

    /**
     * & 或者 ?
     *
     * @return
     */
    public static String getQueryParamterJoinKey(String address) {
        try {
            String url = URLDecoder.decode(address, "UTF-8");
            return url.indexOf("?") == -1 ? "?" : "&";
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException("解析地址失败");
        }
    }

    /**
     * 获取日志
     *
     * @param logId
     * @param offset
     * @return
     */
    @Override
    public ApiLogDomain getOfflineSyncLog(Long logId, Integer offset) {
        return taskConfigServiceImpl.getOfflineSyncLog(logId, offset);
    }

    @Override
    public void stop(Long logId) {
        ApiRunningRecordDomain apiRunningRecord = apiRunningRecordService.getApiRunningRecord(logId);
        String ipAddress = apiRunningRecord.getIpAddress();
        HashMap<String, String> paramMap = Maps.newHashMap();
        paramMap.put("logId", String.valueOf(logId));
        boolean canStop =
            distributedService.stopTaskPost(
                ipAddress, paramMap, OfflineDevTaskStopEnum.API_SYNC.getCode());
        if (!canStop) {
            dataXService.stopApiWs(logId);
        }
    }

    @Override
    public Boolean ignoreApiRunningRecord(String ids) {
        if (StringUtils.isNotBlank(ids)) {
            List<Long> res = Arrays.stream(ids.split(",")).map(e -> Long.valueOf(e))
                .collect(Collectors.toList());
            List<ApiRunningRecordEntity> apiRunningRecordEntities = apiRunningRecordService.listByIds(
                res);
            apiRunningRecordEntities.forEach(e -> {
                e.setIgnored(1);
                e.setTaskStatus(
                    OfflineDevJobStatusEnum.IGNORED.getCode());
            });
            apiRunningRecordService.updateBatchById(apiRunningRecordEntities);
        }
        return true;
    }

    //    @BizDataIndexAnnotation(
//        type = BizDataIndexTypeEnum.API_WS_COLLECT,
//        dataIdFieldName = "id",
//        dataIdParamIndex = 0,
//        operateType = BizDataIndexAnnotation.OperateType.DELETE)
    @Override
    public void delete(Long id) {
        log.info("taskId:{}", id);
        ApiWsEntity task = this.getById(id);
        if (null == task) {
            throw new BusinessException("检查任务状态");
        }
        if (null != task.getXxlJobId()) {
            Integer xxlJobId = task.getXxlJobId();
            xxlJobService.remove(xxlJobId);
        }
        this.update(
            new LambdaUpdateWrapper<ApiWsEntity>()
                .eq(ApiWsEntity::getId, id)
                .set(ApiWsEntity::getRowState, 0));
    }

    @Override
    public Long run(Long taskId) {
        return dataXService.executeApiWs(taskId, OfflineSyncExecuTypeEnum.MANUAL.getStatus(), null);
    }

    /**
     * 任务运行后更改状态 和 启动时间
     *
     * @param id
     */
    @Override
    public void updateAfterStart(Long id) {
        ApiWsEntity apiWsEntity = new ApiWsEntity();
        apiWsEntity.setTaskStatus(ApiTaskStatus.running.getTaskStatus());
        apiWsEntity.setStartTime(new Date());
        apiWsEntity.setEndTime(null);
        this.update(apiWsEntity, new LambdaUpdateWrapper<ApiWsEntity>().eq(ApiWsEntity::getId, id));
    }

    /**
     * 任务结束更新状态和结束时间
     *
     * @param logId
     */
    @Override
    public void updateAfterStop(Long logId, Integer status) {
        ApiRunningRecordDomain apiRunningRecord = apiRunningRecordService.getApiRunningRecord(logId);
        if (null == apiRunningRecord) {
            throw new BusinessException("任务运行记录不存在");
        }
        ApiWsEntity apiWsEntity = new ApiWsEntity();
        apiWsEntity.setTaskStatus(status);
        apiWsEntity.setEndTime(new Date());
        this.update(
            apiWsEntity,
            new LambdaUpdateWrapper<ApiWsEntity>().eq(ApiWsEntity::getId, apiRunningRecord.getApiId()));
    }

    @Override
    public List<TaskDomain> getListByPage(Long projectId, Integer pageSize, Integer pageNum) {
        Page<ApiWsEntity> page =
            this.page(
                new Page<>(pageNum, pageSize),
                new LambdaQueryWrapper<ApiWsEntity>()
                    .eq(ApiWsEntity::getProjectId, projectId)
                    .eq(ApiWsEntity::getRowState, 1));
        return ConvertUtil.copyProperties(page.getRecords(), TaskDomain.class);
    }

    @Override
    public List<ApiWsEntity> getApiTaskByProjectId(Long projectId) {
        return this.list(
            new LambdaQueryWrapper<ApiWsEntity>().eq(ApiWsEntity::getProjectId, projectId));
    }

    @Override
    public String buildApiDataxJson(Long id, Map<String, String> param) {
        ApiWsEntity byId = this.getById(id);
        ApiWsRequest apiWsRequest = byId.getApiWsRequest();
        ApiResponseEntity apiResponse = byId.getApiResponse();
        TaskConfigParamEntity taskConfigParam = byId.getTaskConfigParam();

        TestApiWsParam apiWsParam = Convert.convert(TestApiWsParam.class, apiWsRequest);
        PreviewExpressParam previewExpressParam = Convert.convert(PreviewExpressParam.class,
            apiResponse);
        TaskConfigParam task = Convert.convert(TaskConfigParam.class, taskConfigParam);

        if (MapUtils.isNotEmpty(param)) {
            ApiHttpRequestBodyParam apiWsRequestBodyParam = apiWsRequest.getApiWsRequestBodyParam();
            List<FormParam> formParams = apiWsRequestBodyParam.getFormParams();
            if (CollectionUtils.isEmpty(formParams)) {
                formParams = Lists.newArrayList();
            }
            Set<Entry<String, String>> entries = param.entrySet();
            Iterator<Entry<String, String>> iterator = entries.iterator();
            while (iterator.hasNext()) {
                Entry<String, String> next = iterator.next();
                String key = next.getKey();
                String value = next.getValue();
                FormParam formParam = new FormParam();
                formParam.setParamType(0);
                formParam.setParamName(key);
                formParam.setParamValue(value);
                formParam.setParamDataType("String");
                formParams.add(formParam);
            }
        }
        DataX.JobContentReader jobContentReader = buildJobReader(apiWsParam, previewExpressParam, task);
        String jobJsonString = this.buildJobJson(jobContentReader, task);
        return jobJsonString;
    }
}
